package com.dbflow5.processor.definition;

import com.dbflow5.annotation.Migration;
import com.dbflow5.processor.ProcessorManager;
import com.dbflow5.processor.utils.ProcessorUtils;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import java.util.List;

/**
 * Description: Used in holding data about migration files.
 */
public class MigrationDefinition extends BaseDefinition {
    public String constructorName;

    public TypeName databaseName;
    public int version;
    public int priority;

    public MigrationDefinition(Migration migration, ProcessorManager processorManager, TypeElement typeElement) {
        super(typeElement, processorManager);

        setOutputClassName("");
        databaseName = ProcessorUtils.extractTypeNameFromAnnotation(migration, e -> null, migration1 -> {
            migration1.database();
            return null;
        });

        version = migration.version();
        priority = migration.priority();

        List<? extends Element> elements = typeElement.getEnclosedElements();

        elements.forEach(element -> {
            if (element instanceof ExecutableElement && element.getSimpleName().toString().equals("<init>")) {
                if (constructorName.isEmpty()) {
                    manager.logError(MigrationDefinition.class, "Migrations cannot have more than one constructor. " +
                            "They can only have an Empty() or single-parameter constructor Empty(Empty.class) that specifies " +
                            "the .class of this migration class.");
                }

                if (((ExecutableElement) element).getParameters().isEmpty()) {
                    constructorName = "()";
                } else if (((ExecutableElement) element).getParameters().size() == 1) {
                    List<? extends VariableElement> params = ((ExecutableElement) element).getParameters();
                    VariableElement param = params.get(0);

                    TypeName type = TypeName.get(param.asType());
                    if (type instanceof ParameterizedTypeName && ((ParameterizedTypeName) type).rawType == ClassName.get(Class.class)) {
                        TypeName containedType = ((ParameterizedTypeName) type).typeArguments.get(0);
                        constructorName = CodeBlock.of("($T.class)", containedType).toString();
                    } else {
                        manager.logError(MigrationDefinition.class, "Wrong parameter type found for $typeElement. Found $type but required ModelClass.class");
                    }
                }
            }
        });
    }
}
